#include <algorithm>
#include <assert.h>
#include <iostream>

#include "building_type.hpp"
#include "city_summary_dialog.hpp"
#include "government_summary_dialog.hpp"
#include "map_display.hpp"
#include "path_cost_calculators.hpp"
#include "unit.hpp"
#include "unit_summary_dialog.hpp"

map_display::map_display(world& w, government_ptr gov)
    : camera_(w.map()), renderer_(w, *gov), world_(w), government_(gov), mode_(MODE_NONE), xpos_(0), ypos_(0), turn_(0), done_(false)
{
	government_summary_dialog_.reset(new game_dialogs::government_summary_dialog(world_, government_));
}

map_display::~map_display()
{
}

void map_display::play_game()
{
	while(!done_) {
		play_turn();
		++turn_;
	}
}

void map_display::play_turn()
{
	begin_turn(world_);
	cycle_selection();

	if(city_summary_dialog_) {
		city_summary_dialog_->init();
	}

	if(unit_summary_dialog_) {
		unit_summary_dialog_->init();
	}

	if(government_summary_dialog_) {
		government_summary_dialog_->init();
	}

	while(run_cycle() == false) {
	}

	if(done_) {
		return;
	}

	world_.calculate_trades();
	migrations_.clear();
	end_turn(world_, world_.map(), migrations_);
	road_project_.reset();
	world_.pay_workers();
	world_.build_projects();
	++turn_;

	const std::string MoneyItem = "silver";
	int ncitizens = 0, silver = 0;
	foreach(const hex::tile_ptr& t, world_.map().tiles()) {
		ncitizens += t->citizens().size();
		foreach(const_citizen_ptr c, t->citizens()) {
			silver += c->items_owned(MoneyItem);
		}
	}
	std::cerr << "CENSUS " << turn_ << ": " << ncitizens << "; treasury: " << world_.sides().front()->treasury() << " privately held: " << silver << " TOTAL SILVER: " << (silver + world_.sides().front()->treasury()) << "\n";
}

void map_display::build_new_road()
{
	mode_ = MODE_BUILD_ROAD;
	road_path_.clear();
}

void map_display::build_terrain_improvement()
{
	mode_ = MODE_BUILD_IMPROVEMENT;
}

void map_display::build_city()
{
	assert(selected_unit_);
	city_ptr c = world_.add_city(wml::parse_wml(sys::read_file("city.cfg")), selected_unit_->loc(), government_);
	world_.remove_unit(selected_unit_);
	select_city(c);
}

void map_display::recruit_unit(const_unit_type_ptr type)
{
	assert(selected_city_);
	selected_city_->recruit_unit(world_, type);
	if(city_summary_dialog_) {
		city_summary_dialog_->init();
	}
}

void map_display::build_building(const_building_type_ptr type)
{
	std::cerr << "build building! " << type->id() << "\n";
	project_ptr p(new building_project(selected_city_.get(), type));
	selected_city_->add_project(p);
}

void map_display::cycle_selection()
{
	hex::location loc(0, 0);
	if(selected_city_) {
		loc = selected_city_->tile().loc();
	}

	if(selected_unit_) {
		loc = selected_unit_->loc();
	}

	int iterations = world_.map().width()*world_.map().height();
	while(iterations >= 0) {
		int y = loc.y() + 1;
		int x = loc.x();
		if(y == world_.map().height()) {
			y = 0;
			++x;
			if(x == world_.map().width()) {
				x = 0;
			}
		}

		loc = hex::location(x, y);
		if(select(loc)) {
			center_on_hex(loc);
			return;
		}
		--iterations;
	}
}

void map_display::center_on_hex(const hex::location& loc)
{
	camera_.set_pan_x(-tile_translate_x(loc));
	camera_.set_pan_y(-tile_translate_y(loc));
}

bool map_display::run_cycle()
{
	hex::location focus = last_focus_;
	renderer_.get_selected_hex(camera_, &focus);
	if(last_focus_ != focus) {
		last_focus_ = focus;

		if(mode_ == MODE_BUILD_ROAD && road_path_.empty() == false) {
			std::vector<hex::location> path;
			const int cost = hex::find_path(road_path_.back(), focus, road_cost_calculator(world_.map()), &path);
			if(cost > 0) {
				road_project_.reset(new road_project(world_, selected_city_.get(), path));
			}
		}
	}

	SDL_Event event;
	while(SDL_PollEvent(&event)) {
	if(city_summary_dialog_ && city_summary_dialog_->process_event(event, false)) {
		continue;
	}
	if(unit_summary_dialog_ && unit_summary_dialog_->process_event(event, false)) {
		continue;
	}
	switch(event.type) {
	case SDL_QUIT:
		done_ = true;
		return true;
	case SDL_KEYDOWN:
		if(event.key.keysym.sym == SDLK_SPACE) {
			road_project_.reset();
			return true;

		} else if(event.key.keysym.sym == SDLK_r && selected_city_) {
			build_new_road();
		} else if(event.key.keysym.sym == SDLK_c) {
			mode_ = MODE_BUILD_CITY;
		} else if(event.key.keysym.sym == SDLK_e) {
			std::cerr << "eroding...\n";
			world_.map().heights().erode(0.1, 0.5);
		} else if(event.key.keysym.sym == SDLK_ESCAPE) {
			done_ = true;
			return true;
		} else if(event.key.keysym.sym == SDLK_n) {
			cycle_selection();
		} else if(event.key.keysym.sym == SDLK_b && selected_unit_ && selected_unit_->type()->can_build_city()) {
			build_city();
		} else if(event.key.keysym.sym == SDLK_p) {
			camera_.tilt_up();
		} else if(event.key.keysym.sym == SDLK_m) {
			world_.map().heights().invert_normals();
		} else if(event.key.keysym.sym == SDLK_l) {
			camera_.tilt_down();
		}
		break;
	case SDL_MOUSEBUTTONUP: {
		if(mode_ == MODE_NONE) {
			if(selected_unit_ && event.button.button == SDL_BUTTON_LEFT &&
			   unit_moves_.count(focus) != 0) {
				move_unit(focus);
				break;
			}
			if(world_.map().is_loc_on_map(focus)) {
				if(event.button.button == SDL_BUTTON_RIGHT) {
					hex::tile_ptr t = world_.map().get_tile(focus);
					if(t && t->city()) {
						select_city(t->city());
						game_dialogs::city_dialog d(world_, t->city());
						d.show_modal();
						if(government_summary_dialog_) {
							government_summary_dialog_->init();
						}
					}
				} else {
					select(focus);
				}
			}
		} else if(mode_ == MODE_BUILD_CITY) {
			city_ptr new_city = world_.add_city(wml::parse_wml(sys::read_file("city.cfg")), focus, world_.sides().front());
			select_city(new_city);
			mode_ = MODE_NONE;
		} else if(mode_ == MODE_BUILD_ROAD && selected_city_) {
			road_project_.reset();
			if(world_.map().is_loc_on_map(focus) == false) {
				mode_ = MODE_NONE;
				road_path_.clear();
			} else if(road_path_.empty()) {
				hex::const_tile_ptr t = world_.map().get_tile(focus);
				road_path_.push_back(focus);
			} else {
				std::vector<hex::location> path;
				const int cost = hex::find_path(road_path_.back(), focus, road_cost_calculator(world_.map()), &path);
				if(cost >= 0) {
					project_ptr p(new road_project(world_, selected_city_.get(), path));
					selected_city_->add_project(p);
					mode_ = MODE_NONE;
					//road_path_.insert(road_path_.end(), path.begin(), path.end());
				}
			}

			if(city_summary_dialog_) {
				city_summary_dialog_->init();
			}
		} else if(mode_ == MODE_BUILD_IMPROVEMENT && selected_city_) {
			if(world_.map().is_loc_on_map(focus)) {
				hex::tile_ptr t = world_.map().get_tile(focus);
				if(t->can_build_improvement()) {
					project_ptr p(new improvement_project(selected_city_, t));
					selected_city_->add_project(p);
				}
			}

			mode_ = MODE_NONE;
		}
		break;
	}
	}
	}

	if(key_[SDLK_UP]) {
		camera_.pan_up();
	}
	if(key_[SDLK_DOWN]) {
		camera_.pan_down();
	}
	if(key_[SDLK_LEFT]) {
		camera_.pan_left();
	}
	if(key_[SDLK_RIGHT]) {
		camera_.pan_right();
	}

	if(key_[SDLK_z]) {
		camera_.zoom_in();
	}

	if(key_[SDLK_x]) {
		camera_.zoom_out();
	}

	draw_map();
	draw_overlay();

	return false;
}

void map_display::draw_map()
{
	//graphics::prepare_raster();
	camera_.prepare_frame();

	renderer_.draw_world(camera_, last_focus_, migrations_, selected_unit_ ? &unit_moves_ : NULL);
	if(road_project_) {
		road_project_->draw();
	}
}

void map_display::draw_overlay()
{
	graphics::prepare_raster();
	if(government_summary_dialog_) {
		government_summary_dialog_->draw();
	}

	if(city_summary_dialog_) {
		city_summary_dialog_->draw();
	}

	if(unit_summary_dialog_) {
		unit_summary_dialog_->draw();
	}
	SDL_GL_SwapBuffers();
	SDL_Delay(20);
}

bool map_display::select(const hex::location& loc)
{
	city_ptr c = world_.map().get_tile(loc)->city();
	const unit_list& units = world_.units_on_tile(loc);
	if(c && selected_city_ == c) {
		if(units.empty()) {
			return false;
		}

		select_unit(units.front());
		return true;
	}

	int index = std::find(units.begin(), units.end(), selected_unit_) - units.begin();
	if(index != units.size()) {
		++index;
		if(index < units.size()) {
			select_unit(units[index]);
			return true;
		} else if(c) {
			select_city(c);
			return true;
		} else {
			select_unit(units.front());
			return true;
		}
	}

	if(c) {
		select_city(c);
		return true;
	} else if(units.empty() == false) {
		select_unit(units.front());
		return true;
	}

	return false;
}

void map_display::select_city(city_ptr c)
{
	clear_selection();

	selected_city_ = c;
	selected_city_->set_selected(true);
	city_summary_dialog_.reset(new game_dialogs::city_summary_dialog(*this, world_, selected_city_));
}

void map_display::select_unit(unit_ptr u)
{
	clear_selection();
	
	selected_unit_ = u;
	selected_unit_->set_selected(true);
	unit_summary_dialog_.reset(new game_dialogs::unit_summary_dialog(*this, world_, u));
	unit_moves_.clear();
	hex::find_possible_moves(u->loc(), u->movement_left(), move_cost_calculator(world_.map()), unit_moves_);
}

void map_display::clear_selection()
{
	if(selected_unit_) {
		selected_unit_->set_selected(false);
		selected_unit_ = unit_ptr();
		unit_moves_.clear();
		unit_summary_dialog_.reset();
	}

	if(selected_city_) {
		selected_city_->set_selected(false);
		selected_city_ = city_ptr();
		city_summary_dialog_.reset();
	}
}

void map_display::move_unit(const hex::location& dst)
{
	assert(selected_unit_);
	hex::route_map::const_iterator move = unit_moves_.find(dst);
	assert(move != unit_moves_.end());
	world_.remove_unit(selected_unit_);
	
	for(int n = 0; n < move->second.steps.size() - 1; ++n) {
		const int src_x = tile_pixel_x(move->second.steps[n]);
		const int src_y = tile_pixel_y(move->second.steps[n]);
		const int dst_x = tile_pixel_x(move->second.steps[n+1]);
		const int dst_y = tile_pixel_y(move->second.steps[n+1]);
		const int nsteps = 10;
		for(int m = 0; m <= nsteps; ++m) {
			const int xpos = (src_x*(nsteps - m) + dst_x*m)/nsteps;
			const int ypos = (src_y*(nsteps - m) + dst_y*m)/nsteps;
			draw_map();
			selected_unit_->draw(xpos, ypos);
			draw_overlay();
		}
	}

	selected_unit_->set_loc(dst);
	world_.add_unit(dst, selected_unit_);
	selected_unit_->deplete_movement(move->second.cost);

	select_unit(selected_unit_);
	if(!selected_unit_->movement_left()) {
		cycle_selection();
	}
}
